#!/usr/bin/env ruby
#
class RedBook::ConsoleInterface < RedBook::Interface

	include RedBook::Utils
	include RedBook::Messenger

	attr_accessor :operations
	
	def initialize
		@core = RedBook::Core.new
		@operations = []
		@parser = RedBook::ConsoleParser.new
		@command = {}
		@dataset = []
		@operation_output, @previous_operation_output = [], []
	end

	def reset
		@output = []
		@command = {}
	end

	# Parse user command and execute the specified operation
	def process(input)
		reset
		begin
			# Populate the @command hash
			@command = @parser.parse(input)
			if @operations.include? @command[:operation]
				notify(@command.to_xhash_command)
				# Call the specified operation, if it is valid
				send(@command[:operation])
			else
				error("Operation invalid or not specified.")
			end
		rescue Exception => e
			# Rescue exceptions and exits
			if e.is_a? SystemExit then
				exit
			else
				bt = []
				e.backtrace.each { |l|	bt << {:debug => l} } if (@command[:debug] || RedBook::DEBUG)
				notify({:error => "#{e.class.to_s.underscore.titleize}: #{e.to_s}", :_children => bt})
			end
		end
		@previous_operation_output = @operation_output.compact
	end

	protected

	# Exit RedBook
	def quit
		info("Exiting...")
		exit
	end

	
	# Query a log file and populate a dataset, displaying results.
	#
	# <i>Message Format:</i> regular expression (to match the activity message)
	# 
	# <i>Additional Directives</i>
	# * <tt>:with|:tags</tt> - Allows to specify tag-based filters (limited boolean logic accepted)
	# * <tt>:avg</tt> - Calculates the average time spent <i>(Tracker Plugin)</i>
  # * <tt>:total</tt> - Calculates the total time spent <i>(Tracker Plugin)</i>
  # * <tt>:count</tt> - Calculates the number of messages returned <i>(Tracker Plugin)</i>
  # * <tt>:duration</tt> - Calculates the duration of each completed activity <i>(Tracker Plugin)</i>
  # * <tt>:from|:since</tt> - Restrict the activities returned to only those after a specified time expression
  # * <tt>:to|:till|:until</tt> - Restrict the activities returned to only those before a specified time expression
  # * <tt>:silent</tt> - No output will be displayed to the user
  # * <tt>:first|:last</tt> -  Restrict the activities returned to the first/last _N_
  # * <tt>:pending|:completed|:plain</tt> - Restrict the activities returned by status
	def select
		@callbacks.exec :before_select
		# Determine the limits and empty the dataset
		limit = @command[:first] || @command[:last] || @log.length
		raise LoggerError, "Start time is earlier than end time."  if @command[:from] && @command[:to] && @command[:to] < @command[:from]
		start_time = (@command[:from]) ? @core.find_time_limit(@command[:from], :from) : 0
		end_time = (@command[:to]) ? @core.find_time_limit(@command[:to], :to) : @log.length-1
		direction = (@command[:first]) ? :first : :last
		if @command[:pending] then status = :pending
		elsif @command[:completed] then status = :completed
		elsif @command[:plain] then status = :plain
		else status = nil
		end
		@dataset = []
		############ TODO #########
		# 		# Override activity status modifiers in case of an aggregation
		# if (@command[:duration] || @command[:avg] || @command[:total]) then
		#		@command[:plain] = nil
		#		@command[:completed] = nil
		#		@command[:pending] = nil
		# end
		#
		#>> Move to Tracker Plugin (before_select_messages)
		
		# Call core method
		@dataset = @core.get_messages(limit, direction, start_time, end_time, status, flag)
		debug "Messages in Dataset: #{@dataset.length}"
		unless @dataset.empty? then
			unless @callbacks.exec(:select_flag) then ######## TODO check
				count = 0
				# Display activities
				@dataset.each do |m|
					count+=1
					notify(m.to_xhash_activity, {:index => count, :total => @dataset.length})
				end
				obj = (@dataset.length == 1) ? 'activity' : 'activities'
				success "#{@dataset.length} #{obj} loaded into current dataset."
			end
		else
			success 'No activities found matching the criteria you specified.'
		end
		@callbacks.exec :after_select
	end


end


################################ OLD #############
module RedBook
	
	unless const_defined?(:CONFIG) || const_defined?(:TEST_HOME)
		cfg_file = File.exist?("#{ENV['HOME']}/rbconfig.yml") ? "#{ENV['HOME']}/rbconfig.yml" : "#{RedBook::EXEHOME}/rbconfig.yml"
		if cfg_file == "#{RedBook::EXEHOME}/rbconfig.yml" && RedBook::MODE == 'gem' then
			begin
				raise GenericError, "HOME environment variable not set." unless ENV['HOME']
				if File.exist?(ENV['HOME']) then
				File.move("#{RedBook::EXEHOME}/rbconfig.yml", ENV['HOME'])
				else
					raise GenericError, "Unable to move default configuration file to HOME folder"
				end
				puts " Default configuration file was saved to #{ENV['HOME']}"
				RedBook::CONFIG = "#{ENV['HOME']}/rbconfig.yml"
			rescue Exception => e
				puts " #{e.class.to_s.underscore.titleize}: #{e.to_s}"
				puts " Exiting..." 
			end
		else
			RedBook::CONFIG = cfg_file
		end
	end
end

# This class is used as a frontend to access RedBook::Logger from command prompt.
class RedBook::Interface

	include RedBook::Utils

	# Initialize a renderer and a logger, define the RedBook::Logger#interface_input method,
	# add itself as a logger observer and starts the logger.
	def initialize
		@hooks = RedBook::HookCollection.new
		@renderer = RedBook::Renderer.new
		@directives = []
		RedBook::SHORTCUTS.each_pair do |j, k|
			RedBook::define_shortcut(j, k)
		end
		RedBook::Parser.directives.each { |d| @directives << ":#{d.to_s}" }
		@logger = RedBook::Logger.new
		@logger.interface_input = lambda { |sentence, object, regex_positive, regex_negative| input(sentence, object, regex_positive, regex_negative) }
		@logger.add_observer(self)
		@logger.start
		RedBook::LOADED_PLUGINS.each do |p|
			send :"initialize_#{p}" if respond_to?(:"initialize_#{p}")
		end
	end

	# Input method used as RedBook::Logger#interface_input
	def input(sentence, object, regex_positive, regex_negative=nil)
		@renderer.render({:warning => "#{sentence} [type 'e' to exit]: "})
		display
		print "   >> [input]: "
		answer = gets.chomp
		while !answer.match(regex_positive) && !answer.match(/[eE]/) do
			@renderer.render({:warning => "Invalid #{object}!"})
			display
			@renderer.render({:warning => "#{sentence} [type 'e' to exit]: "})
			display
			print "   >> [input]: "
			answer = gets.chomp
		end
		@renderer.reset
		answer = (answer.match(/[eE]/) || answer.match(regex_negative)) ? nil : answer
	end

	# Display logger notifications.
	def update(element, options)
		if element.name == :input then
			input
		else
			### HOOK
			unless @hooks.exec(:update, {:element => element, :options => options}) then
				@renderer.render(element, options)
				display
				@renderer.reset
			end
		end
	end

	# Start the main loop and process user input.
	def start
		while true do
			# Setup completion
			modified_tags = []
			@logger.tags.each do |e|
				modified_tags << '!'+e
				modified_tags << '+'+e
				modified_tags << '-'+e
			end
			completion_proc = lambda do |i|
				(@directives+@logger.tags+modified_tags+RedBook::Config.instance.macros_array).find_all	{ |e| e.to_s.match(/^#{Regexp.escape(i)}/) }
			end
			RedBook::rawline_editor.completion_proc = 	completion_proc
			# Show prompt
			prompt = instance_eval(RedBook::PROMPT) || "[#{RedBook::NAME}]# "
			input = RedBook::rawline_editor.read(prompt)
			# Process the user command
			@logger.process(input)
		end
	end

	private

	def display
		@renderer.output.each { |e| puts e }
	end

end
